// TODO: CODE REVIEW & CLEANUP!
//
// PolicyFileServer.cs
//
// Implements PolicyFileServer and related types.
//

using System;
using System.Xml.Linq;
using HomeUX.Networking;
using HomeUX.Utilities;

namespace HomeUX.Server
{

// implements a policy file server, which returns information to Silverlight clients about
// what socket connections are permitted
public class PolicyFileServer
{
	WorkQueue _workQueue;
    int _grantToPortNumber;

	SocketServer _socketServer;

	public WorkQueue WorkQueue
	{
		get
		{
			return _workQueue;
		}
		set
		{
			DisallowPropertySetAfterStart("WorkQueue");
			_workQueue = value;
		}
	}

    public int GrantToPortNumber
    {
        get
        {
            return _grantToPortNumber;
        }
        set
        {
            DisallowPropertySetAfterStart("GrantToPortNumber");
            _grantToPortNumber = value;
        }
    }

    /// <summary>
    /// Begins accepting incoming connections.
    /// </summary>
    /// 
    /// <exception cref="FatalException">
    /// The <r>PolicyFileServer</r> could not be started.
    /// </exception>
    ///
    public void Start()
	{
        if (_workQueue == null)
            RequirePropertySetBeforeStart("WorkQueue");
        if (_grantToPortNumber == 0)
            RequirePropertySetBeforeStart("GrantToPortNumber");

        _socketServer = new SocketServer(943, _workQueue, 100, 10000);
        _socketServer.Connected += new ConnectedEventDelegate(policyFileServer_Connected);
        _socketServer.DataReceived += new DataReceivedEventDelegate(
			policyFileServer_DataReceived);
        _socketServer.Disconnected += new DisconnectedEventDelegate(
			_policyFileServer_Disconnected);
        _socketServer.Start(); // throws FatalException on error
	}

    public void Stop()
    {
        if (_socketServer != null)
        {
            _socketServer.Dispose();
            _socketServer = null;
        }
    }

    void policyFileServer_Connected(SocketConnection connection)
    {
        Util.Trace("Policy file server: Connected to: {0}", connection.ClientAddress);
        connection.Tag = new MessagePipe(100, 1.5, new byte[] { (byte)'>' });
    }

    void policyFileServer_DataReceived(SocketConnection connection, byte[] buffer,
		int bytesReceived)
    {
        MessagePipe pipe = (MessagePipe)connection.Tag;
        pipe.AppendBytes(buffer, 0, bytesReceived);
        if (pipe.IsHeaderComplete)
        {
            string message = Util.Utf8ToString(pipe.GetHeader());
			Util.Trace("Policy file server: Received: {0}", message);
            if (message.Contains("<policy-file-request"))
            {
                var reply = new XDocument(
                    //new XDeclaration("1.0", "utf-8", "yes"),
                    new XElement("access-policy",
                        new XElement("cross-domain-access",
                            new XElement("policy",
                                new XElement("allow-from",
                                    new XElement("domain", new XAttribute("uri", "*"))),
                                new XElement("grant-to",
                                    new XElement("socket-resource",
                                        new XAttribute("port", _grantToPortNumber),
                                        new XAttribute("protocol", "tcp")))))));
                    connection.SendBuffer(Util.ToUtf8(reply.ToString()));
            }
            connection.Disconnect();
        }
    }

    void _policyFileServer_Disconnected(SocketConnection connection)
    {
        Util.Trace("Policy file server: Disconnected from: {0}", connection.ClientAddress);
    }

	void RequirePropertySetBeforeStart(string propertyName)
	{
		throw new InvalidOperationException(String.Format(
			"Property \"{0}\" must be set before Start has been called",
			propertyName));
	}

    void DisallowPropertySetAfterStart(string propertyName)
    {
        if (_socketServer != null)
        {
            throw new InvalidOperationException(String.Format(
                "Property \"{0}\" may only be set before Start has been called",
                propertyName));
        }
    }
}

}
